package com.example.springbooteasycode.zjrc.project.Koal.AAAstructure;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.util.Arrays;

/**
 * 模拟Koal执行流程，输入byte[]字节数组
 *
 * @author Csa
 * @time 2025/07/18
 * ：启动AHSMKoalServerMain，可以配合输出响应内容到这个类中。
 */
public class ASimulateKoalProcess {

    // 完全对齐 C 代码的 ascii_table，注意字符是 '0'~'9'、'A'~'F'
    private static final char[] ascii_table = {
            '0','1','2','3','4','5','6','7',
            '8','9','A','B','C','D','E','F'
    };

    public static void main(String[] args) {
        int rlen = 0 ;
        // ===============================初始化=================================
        // 定义一个字节数组，大小为 4096，初始值为 0
        byte[] szCmd = new byte[4096];
        // 打印初始化的byte[]字节数组
//        System.out.println("初始化byte[]字节数组："+Arrays.toString(szCmd));

        // 假设 dealPattern 是 2（可以根据实际情况修改）
        int dealsPattern = 2;
        int dataLen = 37;
        int nCmdLen = 0;

        // 指令代码，0x6AB6 是一个十六进制数
        short sCmd = (short) 0x6AB6;


        // 初始化inbuf[]
        byte[] inbufInit = new byte[] {
               ' ',' ',' ','z',
                '0', '3', '0', '3', '1', '0', '1', '9', '9', '0', '9', '9', '2', '0', '5', '7',
                '9', '7', '0', '1', '0', '0', '0', '5', '3', '9', '8', '9', '1', '3', '4', '9',
                'E', '2', 'E', '2', 'C', 'E', '5', '6', '0', '0', '2', '5', '0', '0', '0', '0',
                '0', '0', '0', '0', '0', '0', '0',  '2', '0', '0', '0', '0',
                '0', '0', '0', '0', '0', '0', '0', '0',  '0', '1', '5', '6', '0', '0',
                '0', '0', '0', '0', '0', '0', '0', '0', '0', '1', '5', '6', '2', '5', '0', '2',
                '2', '6', '0', '0',
                '8', '2', '7', '4', 'E', '7', 'F', '5',
                '7', 'C', '0', '0',
                '0', '0', '0', '5',
                '0', '3', 'A', '0', '0', '0', '0', '0',
                'F', '0', 'F', '0'
        };

        // 假设长度域为=4，截取长度域
        byte[] inbuf  = new byte[inbufInit.length -4];
        System.arraycopy(inbufInit, 4, inbuf, 0, inbuf.length);

        byte[] output = new byte[inbuf.length / 2];

        // 调用转换函数
        int result = str2hex(inbuf, output);
        System.out.println("==============初始化byte[]十六进制字符串==============\n"+bytesToHex(output));
        System.out.println("初始化inbuf[]"+Arrays.toString(output));


        //  消息体相关初始化
        // 假设这些值都是已知的

        // 从索引2开始复制8个字节到DIV1
        byte[] DIV1 = new byte[8];
        System.arraycopy(output, 2, DIV1, 0,8);
//        System.out.println("DIV1:"+bytesToHex(DIV1));//DIV1:10 19 90 99 20 57 97 01
        byte[] DIV2 = new byte[2];
        System.arraycopy(output, 10, DIV2, 0,2);
//        System.out.println("DIV2:"+bytesToHex(DIV2));
        byte[] ARQC_TC_AAC = new byte[8];
        System.arraycopy(output, 12, ARQC_TC_AAC, 0,8);
//        System.out.println("ARQC_TC_AAC:"+bytesToHex(ARQC_TC_AAC));
        int KeyType = 2;
        int KeyIndex = 0;
        int KeyID = 0;
        int DataLen = 37;
        byte[] Data = new byte[DataLen];
        System.arraycopy(output, 22, Data, 0,DataLen);
//        System.out.println("Data:"+bytesToHex(Data));
        byte[] Authorization_code = new byte[0x02];
        System.arraycopy(output, 22+DataLen, Authorization_code, 0,2);
//        System.out.println("Authorization_code:"+bytesToHex(Authorization_code));

        // 初始化szCmdCode
        byte[] szCmdCode = new byte[4096];

        // ==================设置指令长度=======================
        if (dealsPattern == 1) {
            nCmdLen = 24 + dataLen;
        } else if (dealsPattern == 2) {
            nCmdLen = 26 + dataLen;
        } else if (dealsPattern == 3) {
            nCmdLen = 24;
        }

        // 使用提供的 putShort 方法将 nCmdLen + 14 放入 szCmd 的指定位置
        putShort(szCmd, (short) (nCmdLen + 14));
        // 打印指令长度结果以验证
//        System.out.println("szCmd[0]: " + szCmd[0]);
//        System.out.println("szCmd[1]: " + szCmd[1]);
//        System.out.println("+指令长度，byte[]字节数组："+Arrays.toString(Arrays.copyOf(szCmd, 2)));

        //==================设置指令代码=======================
        // 设置指令代码到 szCmd 的偏移量为 2 的位置
        putShort(szCmd, 2, sCmd);

        // 打印指令代码结果以验证
//        System.out.println("szCmd[2]: " + szCmd[2]);
//        System.out.println("szCmd[3]: " + szCmd[3]);
//        System.out.println("+指令代码，byte[]字节数组："+Arrays.toString(Arrays.copyOf(szCmd, 4)));

        // ====================产生消息头随机数=====================
        // 生成8字节的消息头，每个字节为0xff
        byte[] header = new byte[8];
        Arrays.fill(header, (byte) 0xFF);

        // 将消息头复制到szCmd数组中，从偏移量4开始
        System.arraycopy(header, 0, szCmd, 4, 8);

        // 打印消息头和更新后的字节数组
//        System.out.println("消息头：" + bytesToHex(header));
//        System.out.println("+消息头，byte[]字节数组：" + Arrays.toString(Arrays.copyOf(szCmd, 12)));

        // ==========================组消息体=======================
        // 模拟指针操作
        ByteBuffer pTemp = ByteBuffer.wrap(szCmdCode);

        // 交易模式
        // 在 Java 的 ByteBuffer 操作中，put 方法执行后，会自动移动指针（即 position 会自增，增的幅度取决于 put 的数据类型 。
        /**
         * ByteBuffer 的 put 行为
         * ByteBuffer 的 put(byte b) 方法逻辑：
         *
         * 写入 1 个字节到当前 position 位置；
         * 写入完成后，position 自动 +1 ，指向下一个可写入位置。
         */
        pTemp.put((byte) dealsPattern);

        // 分散因子1
        memcpy(szCmdCode, pTemp.position(), DIV1, 0, 8);
        pTemp.position(pTemp.position() + 8); // 移动指针

        // 分散因子2
        memcpy(szCmdCode, pTemp.position(), DIV2, 0, 2);
        pTemp.position(pTemp.position() + 2); // 移动指针

        // ARQC_TC_AAC
        memcpy(szCmdCode, pTemp.position(), ARQC_TC_AAC, 0, 8);
        pTemp.position(pTemp.position() + 8); // 移动指针

        // 密钥类型
        pTemp.put((byte) KeyType);

        // 密钥索引
        pTemp.put((byte) KeyIndex);

        // 密钥ID
        pTemp.put((byte) KeyID);

        // 根据交易模式处理数据
        if (dealsPattern == 1) {
            // 数据长度
            putShort(szCmdCode, pTemp.position(), (short) DataLen);
            pTemp.position(pTemp.position() + 2); // 移动指针

            // 数据
            memcpy(szCmdCode, pTemp.position(), Data, 0, DataLen);
            pTemp.position(pTemp.position() + DataLen); // 移动指针
        } else if (dealsPattern == 2) {
            // 数据长度
            putShort(szCmdCode, pTemp.position(), (short) DataLen);
            pTemp.position(pTemp.position() + 2); // 移动指针

            // 数据
            memcpy(szCmdCode, pTemp.position(), Data, 0, DataLen);
            pTemp.position(pTemp.position() + DataLen); // 移动指针

            // 授权码
            memcpy(szCmdCode, pTemp.position(), Authorization_code, 0, 0x02);
            pTemp.position(pTemp.position() + 0x02); // 移动指针
        } else if (dealsPattern == 3) {
            // 授权码
            memcpy(szCmdCode, pTemp.position(), Authorization_code, 0, 0x02);
            pTemp.position(pTemp.position() + 0x02); // 移动指针
        }

        // 将 szCmdCode 复制到 szCmd 的指定位置
        memcpy(szCmd, 16, szCmdCode, 0, pTemp.position());
//        System.out.println("szCmd-十六进制字符串："+bytesToHex(szCmd));
        // todo-最终可用的十六进制字符串。
        String hexStr = bytesToHex(Arrays.copyOf(szCmd, nCmdLen+16));
        // 最终发送的十六进制字符串
        System.out.println("===========szCmd-十六进制字符串-指定位============\n："+hexStr);
//        System.out.println("szCmd-Ascii码-指定位："+hexToAscii(bytesToHex(Arrays.copyOf(szCmd, nCmdLen+16))));

//        System.out.println("szCmd字节数组："+Arrays.toString(szCmd));
//        System.out.println("最终指令长度："+(nCmdLen+16));

        String bytesStr = Arrays.toString(Arrays.copyOf(szCmd, nCmdLen+16));
        System.out.println("szCmd字节数组,简化："+bytesStr);
        // 发送数据
        byte[] byteSend = Arrays.copyOf(szCmd, nCmdLen+16);

        // 发送前置判断，数据长度是否满足发送条件
        int processResult;
        // 校验数据长度（前置判断）
        if (byteSend.length < nCmdLen+16) {
            processResult= ErrorCodes.ERR_CMD_SEND; // 数据不足
            System.out.println("数据不足");
        }

        // ==============================模拟请求加密机的socket过程及返回================================
        try (Socket socket = new Socket("localhost", 8888)) {
            System.out.println("已连接到服务器：" + socket.getInetAddress());

            // 发送数据到服务器
            OutputStream os = socket.getOutputStream();
            os.write(byteSend);

            // 接收服务器返回数据
            InputStream is = socket.getInputStream();
            byte[] byteReceive = new byte[1024];
            int nReceiveLen = is.read(byteReceive);
//            System.out.println("已接收到数据：" + Arrays.toString(byteReceive));
//            System.out.println("已接收到数据长度：" + nReceiveLen);
            System.out.println("=================已接收到原始数据================：\n" + Arrays.toString(Arrays.copyOf(byteReceive, nReceiveLen)));

            byte[] byteReceiveFinally = Arrays.copyOf(byteReceive, nReceiveLen);

            // ============================接收数据判断处理========================
            // 按照 C 代码逻辑处理返回数据
            processResult = processReceivedData(byteReceive, nReceiveLen);
            if (processResult == ErrorCodes.ERR_RES_NONE) {
                System.out.println("数据接收处理成功");
                System.out.println("处理结果：" + processResult);
            } else if (processResult == ErrorCodes.ERR_CMD_TIMEOUT) {
                System.out.println("接收数据超时");
                System.out.println("处理结果：" + processResult);
            } else if (processResult == ErrorCodes.ERR_RES_DATA) {
                System.out.println("数据格式错误或接收失败");
                System.out.println("处理结果：" + processResult);
            }

            // ============================调用 checkRetCode 做完整校验========================
            // 2. 调用 checkRetCode 做完整校验
            byte[] szHead = Arrays.copyOfRange(szCmd, 4, 12); // 从 szCmd 中取消息头（偏移4，长度8）
            int retCode = checkRetCode(sCmd, byteReceiveFinally, szHead);
            if (retCode != ErrorCodes.ERR_RES_NONE) {
                System.out.println("响应校验失败，错误码：" + retCode);
            }
            System.out.println("响应校验成功，返回码：" + retCode);

            // 接收数据:byte[]转为十六进制字符串
            String nReceiveHex = bytesToHex(byteReceiveFinally);
            System.out.println("接收到的数据为：" + nReceiveHex);

            // 定义outBuf，为接收数据截取到的ARPC数据为8位
            byte[] outBuf = new byte[8];
            System.arraycopy(byteReceiveFinally, 13, outBuf, 0, 8);

            byte[] sDataFinally ;

//            // todo-模拟失败
//            retCode =2;
//            processResult=1;

            // 如果 retCode 或者 processResult 为 0 ，则认为接收成功、验证通过
            if (retCode == 0 || processResult == 0) {
                // 3. 校验通过（ret == 0 分支）
                System.out.println("数据接收及响应校验均成功！");
                // 假设有效数据长度（需根据实际逻辑调整）
                 rlen = 8;
                byte[] sData = new byte[1 + rlen * 2];
                sData[0] = 0x41; // 应答码 "A"

                if (rlen > 0) {
                    byte[] hexToStrBuffer = new byte[rlen * 2];
                    hex2str(outBuf, hexToStrBuffer, rlen);
                    System.arraycopy(hexToStrBuffer, 0, sData, 1, hexToStrBuffer.length);
                }
                System.out.println("最终响应数据：" + Arrays.toString(sData));
                sDataFinally = sData;
            }
            else {
                // 模拟错误响应组装（ret != 0 分支）
                byte[] sData = new byte[3];
                sData[0] = 0x45; // 应答码 "E"
                sData[1] = (byte) ((ErrorCodes.ERR_BASE_HSMERR >> 8) & 0xFF); // 假设 ERR_BASE_HSMERR 是定义的常量
                sData[2] = (byte) (retCode & 0xFF);
                System.out.println("错误响应数据：" + Arrays.toString(sData));
                sDataFinally = sData;
            }

            // =========================最终响应返回数据========================
            System.out.println("==================最终响应数据====================");
            System.out.println("最终响应数据：" + Arrays.toString(sDataFinally));
            // 转为十六进制字符串
            String sDataFinallyHex = bytesToHex(sDataFinally);
            System.out.println("最终响应数据十六进制：" + sDataFinallyHex);
            // 转为ascii码
            String sDataFinallyAscii = hexToAscii(sDataFinallyHex);
            System.out.println("最终响应数据ASCII码：" + sDataFinallyAscii);
            // ascii码转为byte[]
            byte[] sDataFinallyBytes = sDataFinallyAscii.getBytes();
            System.out.println("最终响应数据byte[]：" + Arrays.toString(sDataFinallyBytes));


        } catch (IOException e) {
            e.printStackTrace();
        }

    }


    /**
     * 严格模拟 C 版 hex2asc：用 ascii_table 数组映射，确保和 C 完全对齐
     * @param dat    要转换的字节（取高4位、低4位）
     * @param newArr 输出缓冲区（存储转换后的 ASCII 字符）
     * @param index  写位置索引（用 int[] 模拟指针，长度为1存当前索引）
     */
    public static void hex2asc(byte dat, byte[] newArr, int[] index) {
        int highIndex = (dat >> 4) & 0x0F;
        newArr[index[0]] = (byte) ascii_table[highIndex];
        index[0]++;

        int lowIndex = dat & 0x0F;
        newArr[index[0]] = (byte) ascii_table[lowIndex];
        index[0]++;
    }

    /**
     * 模拟 C 版 hex2str：逐字节拆高、低4位，用 ascii_table 映射
     * @param read  输入字节数组（待转换的二进制数据）
     * @param write 输出缓冲区（用 int[] 模拟指针，存储当前写入位置索引）
     * @param len   要转换的字节长度
     */
    public static void hex2str(byte[] read, byte[] write, int len) {
        int[] index = {0}; // 模拟指针，初始位置0
        for (int i = 0; i < len; i++) {
            hex2asc(read[i], write, index);
        }
    }



    /**
     * 模拟 C 代码的 CheckRetCode 逻辑
     * 校验：应答码、消息头、响应码
     * @param sCmd       指令码（short）
     * @param szRes      响应数据（byte[]）
     * @param szHead     消息头（byte[]，长度8）
     * @return           错误码（符合 ErrorCodes 定义）
     */
    public static int checkRetCode(short sCmd, byte[] szRes, byte[] szHead) {
        // 1. 校验应答码（sCmd + 1 是否等于 szRes[2..3] 的 short 值）
        if (szRes.length < 4) {
            return ErrorCodes.ERR_RES_DATA;
        }
        short respCmd = getShort(szRes, 2); // 从 szRes[2] 开始读 short
        if (sCmd + 1 != respCmd) {
            return ErrorCodes.ERR_RES_DATA;
        }

        // 2. 校验消息头（szRes[4..11] 是否等于 szHead）
        if (szRes.length < 12 || szHead.length != 8) {
            return ErrorCodes.ERR_RES_DATA;
        }
        for (int i = 0; i < 8; i++) {
            if (szRes[4 + i] != szHead[i]) {
                return ErrorCodes.ERR_RES_DATA;
            }
        }

        // 3. 处理响应码（szRes[12]）
        if (szRes.length < 13) {
            return ErrorCodes.ERR_RES_DATA;
        }
        short cRetCode = (short) (szRes[12] & 0xFF); // 取响应码
        cRetCode = retcode2Code(cRetCode); // 模拟 Retcode2Code 转换（需实现）

        if (cRetCode != ErrorCodes.ERR_RES_NONE) {
            // 记录日志（模拟 WriteSysLog）
            System.out.printf("HSM ret failed: retcode = %d%n", cRetCode);
            return cRetCode;
        }

        return ErrorCodes.ERR_RES_NONE;
    }

    /**
     * 从 byte[] 中读取 short（大端序，类似 C 的 GetShort）
     * @param data   字节数组
     * @param offset 起始偏移
     * @return       short 值
     */
    public static short getShort(byte[] data, int offset) {
        if (data.length < offset + 2) {
            return 0;
        }
        return (short) (((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF));
    }

    /**
     * 模拟 C 语言的 Retcode2Code 函数，根据 RetCode 值映射到对应的错误码
     * @param retCode 原始响应码（unsigned short，Java 中用 short 承载，注意范围）
     * @return 转换后的错误码（对应 ErrorCodes 中的常量）
     */
    public static short retcode2Code(short retCode) {
        switch (retCode) {
            case 0x00:
                return ErrorCodes.ERR_RES_NONE;
            case 0x01:
            case 0xfd:
                return ErrorCodes.ERR_PDATA;
            case 0x02:
            case 0x03:
            case 0x04:
            case 0x0a:
            case 0x0b:
            case 0x0c:
            case 0x0d:
            case 0x0e:
                return ErrorCodes.ERR_KEY;
            case 0x05:
            case 0x06:
                return ErrorCodes.ERR_RES_DATA;
            case 0x07:
                return ErrorCodes.ERR_PRINTKEY;
            case 0x08:
            case 0x09:
                return ErrorCodes.ERR_OTHER;
            case 0x0f:
                return ErrorCodes.ERR_PARAMETER;
            case 0xff:
                return ErrorCodes.ERR;
            default:
                return ErrorCodes.ERR;
        }
    }

    /**
     * 模拟 C 中发送数据的逻辑，包含数据长度校验、循环发送、错误码返回
     * :
     * Java 中不需要（也无法）获取 write() 的返回值来判断发送长度，直接发送即可。
     * todo:这个方法可以不用。
     * @param socket
     * @param szDataIn
     * @param nDataInLen
     * @return
     * @throws IOException
     */
    public static int sendData(Socket socket, byte[] szDataIn, int nDataInLen) throws IOException {
        OutputStream os = socket.getOutputStream();
        int totalSent = 0;

        // 校验数据长度
        if (szDataIn.length < nDataInLen) {
            return ErrorCodes.ERR_CMD_SEND;
        }

        // 模拟 C 的循环发送（虽然 Java 不需要，但为了逻辑对齐）
        while (totalSent < nDataInLen) {
            int remaining = nDataInLen - totalSent;
            // 计算实际能发送的字节数（Java 中无法直接获取，这里假设全部发送）
            int sentNow = remaining;

            // 实际发送数据
            os.write(szDataIn, totalSent, remaining);
            os.flush();

            // 模拟 C 的 datlen 判断（Java 中无法准确获取，这里强制推进）
            if (sentNow <= 0) {
                return ErrorCodes.ERR_CMD_SEND;
            }
            totalSent += sentNow;
        }

        // 校验总发送长度
        if (totalSent != nDataInLen) {
            return ErrorCodes.ERR_CMD_SEND;
        }

        return ErrorCodes.ERR_RES_NONE;
    }


    /**
     * 按照 C 代码逻辑处理接收到的数据
     *
     * @param byteReceive  接收到的字节数组
     * @param nReceiveLen  实际接收到的数据长度
     * @return 错误码，对应 C 代码逻辑
     */
    public static int processReceivedData(byte[] byteReceive, int nReceiveLen) {
        // 先读取前 2 个字节，类似 C 代码中先 recv 2 字节获取部分长度信息
        if (nReceiveLen < 2) {
            return ErrorCodes.ERR_RES_DATA;
        }
        byte[] tempBuf = Arrays.copyOf(byteReceive, 2);
        // 计算期望的总长度，类似 C 代码中 len = ((szDataOut[0]<<8)+szDataOut[1])+2;
        int len = ((tempBuf[0] & 0xFF) << 8) | (tempBuf[1] & 0xFF) + 2;
        int totalReceived = 2;

        // 继续读取剩余数据，类似 C 代码中的 while 循环
        while (totalReceived < len && totalReceived < nReceiveLen) {
            int remaining = len - totalReceived;
            int copyLen = Math.min(remaining, nReceiveLen - totalReceived);
            if (copyLen <= 0) {
                return ErrorCodes.ERR_RES_DATA;
            }
            totalReceived += copyLen;
        }

        // 检查是否读取到足够长度数据
        if (totalReceived < len) {
            return ErrorCodes.ERR_RES_DATA;
        }

        // 这里可以添加类似写日志的操作，比如打印接收的数据十六进制等
        System.out.println("#####RECV 处理后数据: " + Arrays.toString(Arrays.copyOf(byteReceive, totalReceived)));

        return ErrorCodes.ERR_RES_NONE;
    }

    /**
     * 模拟 C 语言中的 memcpy 函数
      */
    public static void memcpy(byte[] dest, int destPos, byte[] src, int srcPos, int length) {
        System.arraycopy(src, srcPos, dest, destPos, length);
    }

    /**
     * 模拟putShort
     * @param dst
     * @param src
     */
    public static void putShort(byte[] dst, short src) {
        // 将 short 类型的高 8 位放到 dst[0]
        dst[0] = (byte) ((src >> 8) & 0xFF);
        // 将 short 类型的低 8 位放到 dst[1]
        dst[1] = (byte) (src & 0xFF);
    }

    /**
     * 模拟putShort-升级版
     * ：放入到指定到字节数组的指定位置
     * 将 short 类型的数据写入到指定的字节数组的指定位置
     * @param dst 目标字节数组
     * @param offset 写入的起始位置
     * @param src 要写入的 short 类型数据
     */
    public static void putShort(byte[] dst, int offset, short src) {
        // 将 short 类型的高 8 位放到 dst[offset]
        dst[offset] = (byte) ((src >> 8) & 0xFF);
        // 将 short 类型的低 8 位放到 dst[offset + 1]
        dst[offset + 1] = (byte) (src & 0xFF);
    }

    /**
     * 将字节数组转换为十六进制字符串表示
     * todo：不是主逻辑，辅助打印
     */
    private static String bytesToHex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte b : bytes) {
            // 输出有空格
//            result.append(String.format("%02X ", b));
            // 输出无空格
            result.append(String.format("%02X", b));
        }
        return result.toString();
    }

    /**
     * 十六进制字符串转为 ASCII码
     * @param hexStr
     * @return
     */
    public static String hexToAscii(String hexStr) {
        StringBuilder output = new StringBuilder();

        // 遍历十六进制字符串，每次处理两个字符
        for (int i = 0; i < hexStr.length(); i += 2) {
            // 提取两个字符作为一个十六进制值
            String str = hexStr.substring(i, i + 2);
            // 将十六进制值转换为对应的整数
            int decimal = Integer.parseInt(str, 16);
            // 将整数转换为对应的 ASCII 字符
            output.append((char) decimal);
        }

        return output.toString();
    }

    /**
     * 将ASCII字符串形式的十六进制数据转换为二进制形式
     * todo：参考HexConverter
     * @param input  输入的ASCII字符串（字节数组形式）
     * @param output 输出的二进制数据（字节数组形式）
     * @return 0表示成功，-1表示失败（包含非法字符）
     */
    public static int str2hex(byte[] input, byte[] output) {
        if (input.length % 2 != 0) {
            return -1; // 输入长度必须为偶数
        }

        int outputIndex = 0;
        for (int i = 0; i < input.length; i += 2) {
            // 解析高4位
            Byte highNibble = asc2hex(input[i]);
            if (highNibble == null) return -1;

            // 解析低4位
            Byte lowNibble = asc2hex(input[i + 1]);
            if (lowNibble == null) return -1;

            // 合并为一个字节：高4位左移4位 + 低4位
            output[outputIndex++] = (byte) ((highNibble << 4) | lowNibble);
        }
        return 0;
    }

    /**
     * 将单个ASCII字符转换为对应的十六进制值
     * @param bKey 输入的ASCII字符
     * @return 对应的十六进制值（0-15），若字符非法则返回null
     */
    private static Byte asc2hex(byte bKey) {
        if (bKey >= '0' && bKey <= '9') {
            return (byte) (bKey - '0');
        } else if (bKey >= 'A' && bKey <= 'F') {
            return (byte) (bKey - 'A' + 10);
        } else if (bKey >= 'a' && bKey <= 'f') {
            return (byte) (bKey - 'a' + 10);
        } else {
            return null; // 非法字符
        }
    }

}
